\documentclass[12pt,a4paper]{article}
% \usepackage[UTF8]{ctex}
\usepackage{fontspec}
\usepackage{titletoc}
\usepackage{xeCJK}
\usepackage{graphicx}
\usepackage{amsmath}
\usepackage{indentfirst} % 中文首段缩进
\usepackage[outputdir=latex.out]{minted} % 代码块高亮渲染

\setmainfont{Source Han Sans CN}
\setCJKmainfont{Source Han Sans CN}
% \setmainfont[Path=/usr/share/fonts/wenquanyi/wqy-microhei/]{wqy-microhei.ttc}
% \setCJKmainfont[Path=/usr/share/fonts/wenquanyi/wqy-microhei/]{wqy-microhei.ttc}
% \setmainfont[Path=/mnt/data/Fonts/NotoSans/]{NotoSans-Regular.ttf}
% \setCJKmainfont[Path=/mnt/data/Fonts/NotoSans/]{NotoSans-Regular.ttf}

% 图片位置
\graphicspath{ {img/} }

\usepackage{xcolor}
\usepackage{hyperref}
\hypersetup{
    colorlinks=true,
    linkcolor=blue,
    urlcolor=cyan,
    linktoc=all,
	pdftitle={操作系统 Lab5 实验报告},
	bookmarks=true
}

% 行内代码背景颜色和相关快捷命令
\definecolor{bg}{rgb}{0.95,0.95,0.95}
\newmintinline[incode]{text}{bgcolor=bg,breaklines,breakanywhere}
% \newcommand{\incode}[1]{\begin{verbatim}#1\end{verbatim}} % 定义行内代码渲染命令
% \newcommand{\codefile}[1]{\inputminted[bgcolor=bg,linenos,tabsize=4]{verilog}{code/#1}} % 定义行内代码渲染命令
	
\renewcommand{\baselinestretch}{1.1} % 定义行间距
\parindent 24pt % 重新定义缩进长度
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  lengths
%    下面的命令重定义页面边距，使其符合中文刊物习惯。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\addtolength{\topmargin}{-54pt}
\setlength{\oddsidemargin}{-0.9cm}
\setlength{\evensidemargin}{\oddsidemargin}
\setlength{\textwidth}{18.00cm}
\setlength{\textheight}{25.50cm}    % 24.62

% 标题、作者、日期等信息
\title{操作系统~Lab5~实验报告}
\author{张海斌\thanks{学号 17307130118}}
\date{2019年11月}


\begin{document}

\maketitle

% 目录
\renewcommand\contentsname{目~录} % 目录名称
\tableofcontents

\section{Exercise~1}

\incode{proc_init()}函数中初始化了\incode{ptable.lock}与\incode{ptable.proc}中每个进程的状态\incode{state}。

\incode{proc_alloc()}函数中，从\incode{ptable.lock}中寻找未使用过的数据项进行进程的初始化操作，构建页表、trap frame和context等。

\incode{ucode_load()}函数将内存中的ELF文件内容读取到用户内存空间中，与此同时补充所需页表映射，并为用户代码的执行提供一个用户线程的栈。在构建页表的过程中，使用\incode{region_alloc()}函数构造连续的页表映射。复制ELF文件时，使用\incode{uvm_switch()}函数将当前页表切换为用户的页表，方便连续写入内存。

\incode{ucode_run()}函数从\incode{ptable.proc}进程列表中寻找可以执行的进程，并切换到用户页表，通过\incode{swtch()}函数进入用户内核线程，再进入到用户线程。

\incode{user_init()}函数使用\incode{proc_alloc()}函数和\incode{ucode_load()}函数初始化用户进程。

最后，\incode{swtch.S}文件中完成了\incode{swtch()}函数的汇编代码，实现了上下文切换的功能。

\section{Exercise~2}

\incode{sched()}函数从内核进程切换到用户内核线程；\incode{forkret()}函数关键是使用\incode{return}语句进行跳转；\incode{exit()}函数是用户程序退出时执行的函数，需要修改用户进程的状态为\incode{ZOMBIE}，同时在内核调度器中需要对其进行处理，删除进程所占用的资源。\incode{sys_cputs()}函数调用\incode{cputchar()}函数来打印指定长度的字符串。\incode{sys_call()}函数根据系统调用号来选择对应的函数和参数来处理系统调用。\incode{trap()}函数中对于\incode{T_SYSCALL}的中断，调用\incode{sys_call()}函数处理。

在这次Lab中，遇到了之前中断Lab中无法正常触发中断的问题，也遇到了内存分配中，\incode{pde_t}没有分配用户权限导致的用户无权限访问内存的问题，也遇到了很多段寄存器数值设置错误的问题。总之，遇到了很多bug。

\section{Exercise~3}

\textbf{Question:} What dose pushcli() and popcli() do? And why?

\textbf{Answer:} \incode{pushcli()}函数的作用是关闭中断，同时将EFLAGS寄存器中的中断标志位置1保存到CPU状态中，同时用\incode{thiscpu->ncli}对中断次数作记录。\incode{popcli()}函数的作用是\incode{popcli()}的反向操作，即恢复中断。它先判断中断是否开启，如果中断已开启则报错，否则减小\incode{thiscpu->ncli}，若减一之后它小于0也报错。只有当\incode{thiscpu->ncli}减为零且\incode{thiscpu->intena}非零（已被初始化过）时才恢复中断。

原因是CPU执行OS代码过程中，可能出现重复关闭或开启中断的情况。为了确保符合程序预期执行结果，需要通过使用栈的机制实现递归式的中断控制。

\end{document}
